package com.xuyuan.spring.async;

import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.ThreadPoolExecutor;

@EnableAsync
@Configuration
public class CustomAsyncConfig implements AsyncConfigurer {

    /**
     * 线程名字的前缀
     */
    public static final String PREFIX = "Custom-";

    @Override
    public ThreadPoolTaskExecutor getAsyncExecutor() {
        return customExecutor();
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return getInstance();
    }

    private ThreadPoolTaskExecutor customExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setKeepAliveSeconds(1000);
        executor.setQueueCapacity(100);
        // 线程名字的前缀，用于标识线程池中的线程
        executor.setThreadNamePrefix(PREFIX);
        /*
         * 当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略
         * 通常有以下四种策略：
         * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
         * ThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。
         * ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
         * ThreadPoolExecutor.CallerRunsPolicy：重试添加当前的任务，自动重复调用 execute() 方法，直到成功
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 自定义的工厂直接处理，子线程异常信息打印 ｜ UncaughtExceptionHandler
        executor.setThreadFactory(new DefaultThreadFactory(PREFIX + "Thread-Pool"));
        // 初始化线程池
        executor.initialize();
        return executor;
    }

    /**
     * 显示设置线程因未捕获异常终止的处理程序
     */
    @Slf4j
    private static class GlobalUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {

        private static final GlobalUncaughtExceptionHandler instance;

        static {
            instance = new GlobalUncaughtExceptionHandler();
        }

        private GlobalUncaughtExceptionHandler() {
        }

        @Override
        public void handleUncaughtException(Throwable ex, Method method, Object... params) {
            log.error("线程：{}，发生异常：{}", Thread.currentThread().getName(), ex.getMessage());
        }


    }

    private static GlobalUncaughtExceptionHandler getInstance() {
        return GlobalUncaughtExceptionHandler.instance;
    }

}